﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Instrument.cs" company="">
//   
// </copyright>
// <summary>
//   The instrument.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;

namespace Systemathics.FrameWork
{
    public sealed class Instrument : ICloneable, IDisposable
    {
        public String Ticker { get; private set; }
        public Guid Id { get; private set; }
        public StaticData StaticData { get; private set; }
        public MarketData MarketData { get; private set; }
        public Blotter Blotter { get; private set; }
        public Trade Trade { get; private set; }
        public Dictionary<Guid, Order> Orders { get; private set; }
        public DataService DataService { get; private set; }
        public decimal CustomValue1 { get; set; }
        public decimal CustomValue2 { get; set; }
        public bool isTotallyFlat
        {
            get { return InventoryExposition.Balance == 0 && isFlat; }
        }
        public bool isFlat
        {
            get { return WorkingExposition.Balance == 0 & TradedExposition.Balance == 0; }
        }
        public bool isShort
        {
            get { return (WorkingExposition.Balance < 0 || TradedExposition.Balance < 0); }
        }
        public bool isLong
        {
            get { return (WorkingExposition.Balance > 0 || TradedExposition.Balance > 0); }
        }
        public bool HasOstToday 
        { get { return Ost(DateTime.Today) != null ? true : false; } } 
        private readonly OrderService OrderService;
        private readonly RemotingLogClient remotingClient;
        private bool Disposed;
        private Decimal vwap;
        private readonly InstrumentExposition InventoryExposition;
        private readonly InstrumentExposition TheoricExposition;
        private readonly InstrumentExposition TradedExposition;
        private readonly InstrumentExposition WorkingExposition; 

        public event EventHandler<Order> OnOrder;
        public event EventHandler<Trade> OnTrade;
        public event EventHandler<Blotter> OnBlotter;
        public event EventHandler<MarketData> OnMarketData;

        #region constructor

        public Instrument(Guid guid, DataService dataService, OrderService orderService)
        {
            remotingClient = new RemotingLogClient();
            try
            {
                using (var db = new SystemathicsEntities(GlobalVariables.EntitiesConnectionString()))
                {
                    var query = db.DBInstruments.Where(u => u.ID == guid);
                    if (query.Any())
                    {
                        var dbi = query.FirstOrDefault();
                        Id = dbi.ID;
                        StaticData = new StaticData(dbi);
                        StaticData.LoadExchange();
                        StaticData.LoadRules();
                        StaticData.LoadCurrency();
                        if (dataService != null)
                        {
                            DataService = dataService;
                            vwap = 0;
                            Blotter = new Blotter(dbi.ID);
                            Trade = new Trade(dbi.ID);
                            MarketData = new MarketData(dbi.ID);
                            DataService.EmitNewBlotter += DataProviderEmitNewBlotter;
                            DataService.EmitNewMarketData += DataProviderEmitNewMarketData;
                            DataService.EmitNewTrade += DataProviderEmitNewTrade;

                            if (orderService != null)
                            {
                                OrderService = orderService;
                                Orders = new Dictionary<Guid, Order>();
                                OrderService.EmitOrder += OrderServiceEmitOrder;
                                dbi.DBTickers.Load();
                                Ticker = dbi.DBTickers.Where(u => u.Provider == orderService.Name).FirstOrDefault().Ticker;
                            }
                            TheoricExposition = new InstrumentExposition(this);
                            WorkingExposition = new InstrumentExposition(this);
                            TradedExposition = new InstrumentExposition(this);
                            InventoryExposition = new InstrumentExposition(this);
                        }
                        // Must be filled only when instantciating the strategy, because we dont know strategy id until this momment.
                    }
                    else
                        remotingClient.Trace(new FireMessageEventArgs("Instrument Not Found", Time.Now.TimeOfDay));
                }
            }
            catch (Exception ex)
            {
                remotingClient.Trace(new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
            }
        }
        public Instrument(Guid guid)
        {
            remotingClient = new RemotingLogClient();
            try
            {
                using (var db = new SystemathicsEntities(GlobalVariables.EntitiesConnectionString()))
                {
                    var query = db.DBInstruments.Where(u => u.ID == guid);
                    if (query.Any())
                    {
                        var dbi = query.FirstOrDefault();
                        Id = dbi.ID;
                        StaticData = new StaticData(dbi);
                        StaticData.LoadExchange();
                        StaticData.LoadRules();
                        StaticData.LoadCurrency();
                    }
                    else
                        new RemotingLogClient().Trace(new FireMessageEventArgs("Instrument Not Found", Time.Now.TimeOfDay));
                }
            }
            catch (Exception ex)
            {
                remotingClient.Trace(new FireMessageEventArgs(ex, Time.Now.TimeOfDay));
            }
        }
        #endregion

        #region Indexers

        public String this[string provider]
        {
            get
            {
                if (!String.IsNullOrEmpty(Ticker))
                    return Ticker;
                using (var db = new SystemathicsEntities(GlobalVariables.EntitiesConnectionString()))
                {
                    var request = db.DBTickers.Where(
                            delegate(DBTickers t)
                            {
                                return ((Guid)t.DBInstrumentsReference.EntityKey.EntityKeyValues[0].Value) == Id &&
                                       t.Provider == provider;
                            }).AsQueryable();
                    if (request.Any())
                        return (Ticker = request.FirstOrDefault().Ticker);
                    return String.Empty;
                }
            }
        }
        public InstrumentExposition this[Expo style]
        {
            get
            {
                switch (style)
                {
                    case Expo.Theo:
                        return TheoricExposition;
                    case Expo.Working:
                        return WorkingExposition;
                    case Expo.Traded:
                        return TradedExposition;
                    case Expo.Inventory:
                        return InventoryExposition;
                }

                return null;
            }
        }

        #endregion Indexers

        #region static Instrument builders

        //public static Instrument GetInstrument(Guid guid)
        //{
        //    return new Instrument(guid, null, null);
        //}
      
        #endregion static Instrument builders

        #region Instrument's Callbacks

        private void UpdateWorkingExpo()
        {
            WorkingExposition.Init();
            foreach (var o in Orders.Values.Where(u => u.isOpen).ToArray())
                WorkingExposition.Add(o.Side, o.RemainingQuantity, o.TargetPrice);
        }
        private void OrderServiceEmitOrder(object sender, Order e)
        {
            bool updateTradedAndTargetExposure = false;

            if (e.InstrumentId == Id)
            {
                Orders[e.Id] = e.Clone() as Order;
                switch (Orders[e.Id].Status)
                {
                    case OrderStatus.ACK:
                        break;
                    case OrderStatus.ACK_CANCEL:
                        UpdateWorkingExpo();
                        StaticData.Fees.Cancellation(Orders[e.Id].CancelQuantity, Orders[e.Id].TargetPrice);
                        break;
                    case OrderStatus.ACK_MODIFY:
                        UpdateWorkingExpo();
                        StaticData.Fees.Modification(Orders[e.Id].TargetQuantity, Orders[e.Id].TargetPrice);
                        break;
                    case OrderStatus.REJECT_CANCEL:
                        break;
                    case OrderStatus.REJECT_MODIFY:
                        break;
                    case OrderStatus.REJECT:
                        UpdateWorkingExpo();
                        break;
                    case OrderStatus.FILLED:
                        updateTradedAndTargetExposure = true;
                        break;
                    case OrderStatus.PARTIALLY_FILLED:
                        updateTradedAndTargetExposure = true;
                        break;
                }

                if (updateTradedAndTargetExposure)
                {
                    lock (TradedExposition)
                    {
                        TradedExposition.Add(e.Side, e.LastExecution.Quantity, e.LastExecution.Price);
                        StaticData.Fees.Execution(e.LastExecution.Quantity, e.LastExecution.Price);
                        UpdateWorkingExpo();
                    }
                }
                OnOrder(this, Orders[e.Id]);
            }
        }
        private void DataProviderEmitNewTrade(object sender, Trade e)
        {
            if (e.Id == Id)
            {
                Trade = e;
                UpdateMarketData(Trade.Size, Trade.Price);
                if (OnTrade != null)
                    OnTrade(this, Trade);
            }
        }
        private void DataProviderEmitNewMarketData(object sender, MarketData e)
        {
            if (e.Id == Id)
            {
                MarketData = e;
                if (OnMarketData != null)
                    OnMarketData(this, MarketData);
            }
        }
        private void DataProviderEmitNewBlotter(object sender, Blotter e)
        {
            if (e.Id == Id)
            {
                Blotter = e;
                if(OnBlotter != null)
                    OnBlotter(this, Blotter);
            }
        }

        #endregion Instrument's Callbacks

        #region public methods

        public void Suscribe()
        {
            DataService.Suscribe(this);
            OrderService.Suscribe(this);
        }
        public void Unsubscribe()
        {
            DataService.Unsubscribe(this);
            OrderService.Unsubscribe(this);
        }

        public Guid Buy(OrderStyle style, Decimal price, int quantity, OrderExtension ext, Guid strategyId)
        {
            if(ext.Tif == TimeInForce.GoodTillDate && (ext.GoodAfterTime == null ||  ext.GoodTillDate == null))
                remotingClient.Trace(new FireMessageEventArgs(StaticData.Name + " GoodTillDate Order must declare two Dates in Order Extenssion",DateTime.Now.TimeOfDay));
            if(!Enum.IsDefined(typeof(TimeInForce), ext.Tif))
                remotingClient.Trace(new FireMessageEventArgs(StaticData.Name + " No Time In Force Found For this order", DateTime.Now.TimeOfDay));
            else if (style == OrderStyle.StopLimit)
            {
                var so = new SmartOrders(this, strategyId);
                so.StopLimitOrder(OrderSide.Buy, quantity, price);
            }
            else if (style == OrderStyle.Stop)
            {
                var so = new SmartOrders(this, strategyId);
                so.StopMarketOrder(OrderSide.Buy, quantity, price);
            }
            else if(style == OrderStyle.TrailingStopLimit)
            {
                var so = new SmartOrders(this, strategyId);
                if (ext.OffsetAmount != 0) 
                    so.TrailStopLimitOrder(OrderSide.Buy, quantity, price, new decimal(Math.Abs(ext.OffsetAmount)));
                else
                    remotingClient.Trace(new FireMessageEventArgs(StaticData.Name + " Trail Stop Orders must have Offset Amount value (OrderExtension)", DateTime.Now.TimeOfDay));
            }
            else if (style == OrderStyle.TrailingStop)
            {
                var so = new SmartOrders(this, strategyId);
                if (ext.OffsetAmount != 0)
                    so.TrailStopMarketOrder(OrderSide.Buy, quantity, price, new decimal(Math.Abs(ext.OffsetAmount)));
                else
                    remotingClient.Trace(new FireMessageEventArgs(StaticData.Name + " Trail Stop Orders must have Offset Amount value (OrderExtension)", DateTime.Now.TimeOfDay));
            }
            else if (style == OrderStyle.LimitOnClose)
            {
                var so = new SmartOrders(this, strategyId);
                so.LimitOnclose(OrderSide.Buy, quantity, price);
            }
            else if (style == OrderStyle.MarketOnClose)
            {
                var so = new SmartOrders(this, strategyId);
                so.MarketOnclose(OrderSide.Buy, quantity);
            } 
            else
            {
                var o = new Order(Id, StaticData, style, OrderSide.Buy, price, quantity, strategyId){SendTime = Time.Now};
                if (CheckOrderSecurity(o)) 
                {
                    StaticData.Rules.CheckTicksizeRule(ref o, MidpointRounding.AwayFromZero);
                    o.Ext = ext;

                    if (OrderService.Send(o))
                    {
                        WorkingExposition.EntryDate = Time.Now;
                        WorkingExposition.Add(OrderSide.Buy, o.TargetQuantity, o.TargetPrice);
                        Orders[o.Id] = o;
                        return o.Id;
                    }
                }
            }
            return Guid.Empty;
        }
        public Guid Sell(OrderStyle style, Decimal price, int quantity, OrderExtension ext, Guid strategyId)
        {
            if (ext.Tif == TimeInForce.GoodTillDate && (ext.GoodAfterTime == null || ext.GoodTillDate == null))
                remotingClient.Trace(new FireMessageEventArgs(this.StaticData.Name + " GoodTillDate Order must declare two Dateq in Order Extenssion", DateTime.Now.TimeOfDay));
            else if (style == OrderStyle.StopLimit)
            {
                var so = new SmartOrders(this, strategyId);
                so.StopLimitOrder(OrderSide.Sell, quantity, price);
            }
            else if (style == OrderStyle.Stop)
            {
                var so = new SmartOrders(this, strategyId);
                so.StopMarketOrder(OrderSide.Sell, quantity, price);
            }
            else if (style == OrderStyle.TrailingStopLimit)
            {
                var so = new SmartOrders(this, strategyId);
                if (ext.OffsetAmount != 0)
                    so.TrailStopLimitOrder(OrderSide.Sell, quantity, price, new decimal(Math.Abs(ext.OffsetAmount)));
                else
                    remotingClient.Trace(new FireMessageEventArgs(StaticData.Name + " Trail Stop Orders must have Offset Amount value (OrderExtension)", DateTime.Now.TimeOfDay));
            }
            else if (style == OrderStyle.TrailingStop)
            {
                var so = new SmartOrders(this, strategyId);
                if (ext.OffsetAmount != 0)
                    so.TrailStopMarketOrder(OrderSide.Sell, quantity, price, new decimal(Math.Abs(ext.OffsetAmount)));
                else
                    remotingClient.Trace(new FireMessageEventArgs(StaticData.Name + " Trail Stop Orders must have Offset Amount value (OrderExtension)", DateTime.Now.TimeOfDay));
            }
            else if (style == OrderStyle.LimitOnClose)
            {
                var so = new SmartOrders(this, strategyId);
                so.LimitOnclose(OrderSide.Sell, quantity, price);
            }
            else if (style == OrderStyle.MarketOnClose)
            {
                var so = new SmartOrders(this, strategyId);
                so.MarketOnclose(OrderSide.Sell, quantity);
            }       
            else
            {
                var o = new Order(Id, StaticData, style, OrderSide.Sell, price, quantity, strategyId){SendTime = Time.Now};
                if (CheckOrderSecurity(o))
                {
                    o.Ext = ext;
                    StaticData.Rules.CheckTicksizeRule(ref o, MidpointRounding.ToEven);
                    
                    if (OrderService.Send(o))
                    {
                        WorkingExposition.EntryDate = Time.Now;
                        WorkingExposition.Add(OrderSide.Sell, o.TargetQuantity, o.TargetPrice);
                        Orders[o.Id] = o;
                        return o.Id;
                    }
                }
            }
            return Guid.Empty;
        }
        public void Bracket(OrderSide side, int quantity , Decimal takeProfit, Decimal stopLoss, Guid strategyId)
        {
            var so = new SmartOrders(this, strategyId);
            so.BracketOrder(side, quantity, takeProfit, stopLoss );
        }
        public void MarketOnClose(OrderSide side, int quantity, Guid strategyId)
        {
            var so = new SmartOrders(this, strategyId);
            so.MarketOnclose(side, quantity);
        }
        public void LimitOnClose(OrderSide side, int quantity, Decimal price, Guid strategyId)
        {
            var so = new SmartOrders(this, strategyId);
            so.LimitOnclose(side, quantity, price);
        }
        public bool Cancel(Guid orderId)
        {
            if (Orders.ContainsKey(orderId))
            {
                if (!Orders[orderId].isClosed)
                {
                    lock (Orders[orderId])
                    {
                        WorkingExposition.EntryDate = Time.Now;
                        if (OrderService.Cancel(Orders[orderId]))
                            return true;
                    }
                }
            }
            else
                remotingClient.Trace(new FireMessageEventArgs(string.Format("{0} Order ID  not possible to Cancel because no exists in Instrument's order", orderId), Time.Now.TimeOfDay));

            return false;
        }
        public bool ReSend(Guid orderId)
        {
            if (Orders.ContainsKey(orderId))
            {
                var o = Orders[orderId];
                if (Orders[o.Id].isClosed && !o.isModifying && !o.isCancelling)
                {
                    switch (o.Side)
                    {
                        case OrderSide.Buy:
                            Buy(o.Style, o.TargetPrice, o.TargetQuantity, o.Ext, o.StrategyId);
                            break;
                        case OrderSide.Sell:
                            Sell(o.Style, o.TargetPrice, o.TargetQuantity, o.Ext, o.StrategyId);
                            break;
                    }
                }
            }
            return false;
        }
        public bool Modify(Guid orderId, int newQty)
        {
            if (Orders.ContainsKey(orderId))
            {
                var o = Orders[orderId];
                lock (o)
                {
                    if (Orders[o.Id].isOpen)
                    {
                        if (newQty <= o.TotalExecutedQuantity)
                            remotingClient.Trace(
                                new FireMessageEventArgs(
                                    string.Format(
                                        "{0} Order ID {1} : Cannot modify an order with a new Qty {2} lower than already Executed Qty {3}",
                                        o.Style, o.InstrumentId, newQty, o.TotalExecutedQuantity), Time.Now.TimeOfDay));
                        else
                        {
                            var newo = new Order(o.InstrumentId, o.StaticData, o.Style, o.Side, o.TargetPrice, newQty, o.StrategyId){SendTime = Time.Now};
                            if (CheckOrderSecurity(newo))
                            {
                                StaticData.Rules.CheckTicksizeRule(ref newo, MidpointRounding.AwayFromZero);
                                
                                if (OrderService.Modify(o, newo))
                                {
                                    WorkingExposition.Add(o.Side, -o.RemainingQuantity, o.TargetPrice);
                                    WorkingExposition.Add(o.Side, newQty - o.TotalExecutedQuantity, o.TargetPrice);
                                    return true;
                                }
                            }
                            else
                                remotingClient.Trace(
                                    new FireMessageEventArgs(
                                        string.Format(
                                            "{0} Order ID {1} : Cannot modify an order which doesn't already exists",
                                            o.Style, o.InstrumentId), Time.Now.TimeOfDay));
                        }
                    }
                }
            }
            return false;
        }
        public bool Modify(Guid orderId, Decimal newPrice)
        {
            if (Orders.ContainsKey(orderId))
            {
                var o = Orders[orderId];
                lock (o)
                {
                    if (o.isOpen)
                    {
                        var newo = new Order(o.InstrumentId, o.StaticData, o.Style, o.Side, newPrice, o.TargetQuantity, o.StrategyId){SendTime = Time.Now};
                        if (CheckOrderSecurity(newo))
                        {
                            StaticData.Rules.CheckTicksizeRule(ref newo, MidpointRounding.AwayFromZero);

                            if (OrderService.Modify(o, newo))
                            {
                                WorkingExposition.Add(o.Side, -o.RemainingQuantity, o.TargetPrice);
                                WorkingExposition.Add(o.Side, o.RemainingQuantity, newPrice);
                                return true;
                            }
                        }
                        else
                            remotingClient.Trace(
                                new FireMessageEventArgs(
                                    string.Format(
                                        "{0} Order ID {1} : Cannot modify an order which doesn't already exists",
                                        o.Style, o.InstrumentId), Time.Now.TimeOfDay));
                    }
                }
            }
            return false;
        }
        public bool Modify(Guid orderId, Decimal newPrice, int newQty)
        {
            if (Orders.ContainsKey(orderId))
            {
                var o = Orders[orderId];
                lock (o)
                {
                    if (o.isOpen)
                    {
                        if (newQty <= o.TotalExecutedQuantity)
                            remotingClient.Trace(
                                new FireMessageEventArgs(
                                    string.Format(
                                        "{0} Order ID {1} : Cannot modify an order with a new Qty {2} lower than already Executed Qty {3}",
                                        o.Style, o.InstrumentId, newQty, o.TotalExecutedQuantity), Time.Now.TimeOfDay));
                        else
                        {
                            var newo = new Order(o.InstrumentId, o.StaticData, o.Style, o.Side, newPrice, newQty, o.StrategyId){SendTime = Time.Now};
                            if (CheckOrderSecurity(newo))
                            {
                                StaticData.Rules.CheckTicksizeRule(ref newo, MidpointRounding.AwayFromZero);

                                if (OrderService.Modify(o, newo))
                                {
                                    WorkingExposition.Add(o.Side, -o.RemainingQuantity, o.TargetPrice);
                                    WorkingExposition.Add(o.Side, newQty - o.TotalExecutedQuantity, newPrice);
                                    return true;
                                }
                            }
                            else
                                remotingClient.Trace(
                                    new FireMessageEventArgs(
                                        string.Format(
                                            "{0} Order ID {1} : Cannot modify an order which doesn't already exists",
                                            o.Style, o.InstrumentId), Time.Now.TimeOfDay));
                        }
                    }
                }
            }
            return false;
        }
        public int CancelAllOrders()
        {
            int n = 0;
            lock (Orders)
            {
                // (u => u.Status != OrderStatus.FILLED || u.Status != OrderStatus.ACK_CANCEL)
                Order[] targetorders = Orders.Values.Where(u => !u.isClosed).ToArray();
                foreach (Order o in targetorders) // check if status is not filled is not cancelled etc ..bla bla ....
                    n += Cancel(o.Id) ? 1 : 0;
            }

            return n;
        }
        public int CancelAllBuyOrders()
        {
            int n = 0;
            lock (Orders)
            {
                IEnumerable<Order> targetorders = Orders.Values.Where(u => u.Side == OrderSide.Buy);
                foreach (Order o in targetorders) // check if status is not filled is not cancelled etc ..bla bla ....
                    n += Cancel(o.Id) ? 1 : 0;
            }

            return n;
        }
        public int CancelAllSellOrders()
        {
            int n = 0;
            lock (Orders)
            {
                IEnumerable<Order> targetorders = Orders.Values.Where(u => u.Side == OrderSide.Sell);
                foreach (Order o in targetorders) // check if status is not filled is not cancelled etc ..bla bla ....
                    n += Cancel(o.Id) ? 1 : 0;
            }

            return n;
        }
        public void SweepToFill(Expo expo, Guid strategyId)
        {
            var remote = new RemotingLogClient();
            int qtyToFlatten = 0;

            switch (expo)
            {
                case Expo.Traded:
                    qtyToFlatten = this[Expo.Traded].Balance;
                    break;
                case Expo.Inventory:
                    qtyToFlatten = this[Expo.Inventory].Balance;
                    break;
            }
            if (qtyToFlatten > 0)
            {
                if (qtyToFlatten < Blotter.BidVolumeUntil(QuoteLevel.Five))
                {
                    var t = new int[5]; var ql = QuoteLevel.One;
                    var potentialexecPrice = Blotter.WeigthedBidPriceUntil(qtyToFlatten, ref t, ref ql);
                    var targetPrice = Blotter[ql].Bid;
                    Sell(OrderStyle.Market, targetPrice, qtyToFlatten, new OrderExtension { SweepToFill = true }, strategyId);
                    remote.Trace(new FireMessageEventArgs(expo.ToString() + " : Sweep Sell for " + StaticData.Name + " - " + qtyToFlatten + " @ potential price" + potentialexecPrice, DateTime.Now.TimeOfDay));
                }
                else
                    remote.Trace(new FireMessageEventArgs("Qty to Flatten Higher than Bid Volume Until Level 5" + StaticData.Name + " - Size: " + qtyToFlatten, DateTime.Now.TimeOfDay));
            }
            else if (qtyToFlatten < 0)
            {
                qtyToFlatten = -qtyToFlatten;
                if (qtyToFlatten < Blotter.AskVolumeUntil(QuoteLevel.Five))
                {
                    var t = new int[5]; var ql = QuoteLevel.One;
                    var PotentialexecPrice = Blotter.WeigthedAskPriceUntil(qtyToFlatten, ref t, ref ql);
                    var targetPrice = Blotter[ql].Ask;
                    Buy(OrderStyle.Market, targetPrice, qtyToFlatten, new OrderExtension { SweepToFill = true }, strategyId);
                    remote.Trace(new FireMessageEventArgs(expo.ToString() + " : Sweep Buy for " + StaticData.Name + " - " + qtyToFlatten + " @ potentential price" + PotentialexecPrice, DateTime.Now.TimeOfDay));
                }
                else
                    remote.Trace(new FireMessageEventArgs("Qty to Flatten Higher than Ask Volume Until Level 5" + StaticData.Name + " - Size: " + qtyToFlatten, DateTime.Now.TimeOfDay));
            }
        }

        #endregion public methods

        #region private methods
        public Ost? Ost(DateTime dt)
        {
            using (var db = new SystemathicsEntities(GlobalVariables.EntitiesConnectionString()))
            {
                Ost ost;
                IQueryable<DBOst> dbo = db.DBOst.Where(y => y.DBInstruments.ID == Id && y.OstDate == dt.Date);
                if (dbo.Any())
                {
                    ost.Date = dbo.FirstOrDefault().OstDate;
                    ost.Event = dbo.FirstOrDefault().OstEvent;
                    ost.Value = dbo.FirstOrDefault().OstValue;
                    return ost;
                }
                return null;
            }
        }
        private bool CheckOrderSecurity(Order o)
        {
            //if (o.SendTime.TimeOfDay > o.StaticData.Exchange.OpenAuctions &&
            //    o.SendTime.TimeOfDay < o.StaticData.Exchange.CloseAuctions)
            //{
                if (o.StaticData.Rules.isTradable)
                {
                    if (o.StrategyId != Guid.Empty)
                    {
                        if (o.TargetQuantity > 0 && o.TargetPrice > 0)
                        {
                            if (Blotter[QuoteLevel.One].Bid != 0 && Blotter[QuoteLevel.One].Ask != 0)
                            {
                                int holdingPos = WorkingExposition.Balance + TradedExposition.Balance +
                                                 InventoryExposition.Balance;
                                int newholdingPos = holdingPos +
                                                    ((o.Side == OrderSide.Buy) ? o.TargetQuantity : -o.TargetQuantity);

                                switch (o.Side)
                                {
                                    case OrderSide.Buy:

                                        if (!o.StaticData.Rules.isBuyAllowed)
                                        {
                                            remotingClient.Trace(
                                                new FireMessageEventArgs(
                                                    string.Format("{0} Order ID {1} not valid because Buy NOT Allowed",
                                                                  o.Style,
                                                                  o.InstrumentId), Time.Now.TimeOfDay));
                                            return false;
                                        }

                                        break;
                                    case OrderSide.Sell:
                                        if (!o.StaticData.Rules.isSellAllowed)
                                        {
                                            remotingClient.Trace(
                                                new FireMessageEventArgs(
                                                    string.Format(
                                                        "{0} Order ID {1} not valid because Sell NOT Allowed", o.Style,
                                                        o.InstrumentId), Time.Now.TimeOfDay));
                                            return false;
                                        }

                                        if (
                                            !(newholdingPos >= 0 ||
                                              (newholdingPos < 0 && o.StaticData.Rules.isShortable)))
                                        {
                                            remotingClient.Trace(
                                                new FireMessageEventArgs(
                                                    string.Format(
                                                        "{0} Order ID {1} not valid because new holding position will be short and Stock NOT Shortable",
                                                        o.Style, o.InstrumentId), Time.Now.TimeOfDay));
                                            return false;
                                        }

                                        break;
                                }

                                var orderNominal = o.TargetQuantity*Decimal.ToDouble(o.TargetPrice)*
                                                   o.StaticData.Rules.TickValue*o.StaticData.Currency.ReferencePrice;
                                if (orderNominal <= StaticData.Rules.MaxNominalOrder)
                                {
                                    // Be carefull:  Take profit orders, or Stop Orders can be placed far from the market > 30% ?
                                    var dev = Math.Abs((o.TargetPrice - Blotter[QuoteLevel.One].Mid) / o.TargetPrice);
                                    if (Decimal.ToDouble(dev) <= 0.3)
                                        return true;

                                    remotingClient.Trace(
                                        new FireMessageEventArgs(
                                            string.Format(
                                                "{0} Order ID {1} not valid because requested price too far from current prices: {2}",
                                                o.Style, o.InstrumentId, o.TargetPrice), Time.Now.TimeOfDay));
                                }
                                else
                                    remotingClient.Trace(
                                        new FireMessageEventArgs(
                                            string.Format(
                                                "{0} Order ID {1} not valid because Nominal for this order higher than Maximum Nominal allowed",
                                                o.Style, o.InstrumentId), Time.Now.TimeOfDay));
                            }
                            else
                                remotingClient.Trace(
                                    new FireMessageEventArgs(
                                        string.Format(
                                            "{0} Order ID {1} not valid because Blotter empty for this instrument",
                                            o.Style,
                                            o.InstrumentId), Time.Now.TimeOfDay));
                        }
                        else
                            remotingClient.Trace(
                                new FireMessageEventArgs(
                                    string.Format("{0} Order ID {1} not valid because of price or quantity signs",
                                                  o.Style,
                                                  o.InstrumentId), Time.Now.TimeOfDay));
                    }
                    else
                        remotingClient.Trace(
                            new FireMessageEventArgs(
                                string.Format("{0} Order ID {1} No booking path ", o.Style, o.InstrumentId),
                                Time.Now.TimeOfDay));
                }
                else
                    remotingClient.Trace(
                        new FireMessageEventArgs(
                            string.Format("{0} Order ID {1} is NOT Tradable ", o.Style, o.InstrumentId),
                            Time.Now.TimeOfDay));
        //}
        //else
        //remotingClient.Trace(
        //                new FireMessageEventArgs(
        //                    string.Format("{0} Order ID {1} cannot be created & sent outside Exchange Hours ", o.Style, o.InstrumentId),
        //                    Time.Now.TimeOfDay));

        return false;
        }
        private void UpdateMarketData(int qty, Decimal price)
        {
            vwap += price * qty;
            MarketData.Volume += qty;
            MarketData.VWAP = vwap / MarketData.Volume;
            MarketData.TradeCount++;
        }

        #endregion private methods

        #region Instrument operators override

        public override bool Equals(object obj)
        {
            if (obj == null)
                return false;
            var instr = (Instrument)obj;
            return Id == instr.Id;
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public static bool operator ==(Instrument instr1, Instrument instr2)
        {
            return instr1.Equals(instr2);
        }
        public static bool operator !=(Instrument instr1, Instrument instr2)
        {
            return !instr1.Equals(instr2);
        }

        #endregion Instrument operators override

        #region ICloneable Members

        public object Clone()
        {
            return MemberwiseClone();
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            DataService.EmitNewBlotter -= DataProviderEmitNewBlotter;
            DataService.EmitNewMarketData -= DataProviderEmitNewMarketData;
            DataService.EmitNewTrade -= DataProviderEmitNewTrade;

            OrderService.EmitOrder -= OrderServiceEmitOrder;
            Dispose(true);
        }
        private void Dispose(bool disposing)
        {
            if (!Disposed)
            {
                if (disposing)
                {
                    GC.SuppressFinalize(this);
                    Trade = null;
                    Blotter = null;
                    MarketData = null;
                    Orders = null;
                }

                Disposed = true;
            }
        }

        #endregion
    }
}